#
# Copyright 2020, Data61, CSIRO (ABN 41 687 119 230)
#
# SPDX-License-Identifier: BSD-2-Clause
#

# This module provides `DeclareRootserver` for making an executable target a rootserver
# and bundling it with a kernel.elf and any required loaders into an `images` directory
# in the top level build directory.
include_guard(GLOBAL)

# 确保 app 应用的 tls_app.lds 存在
find_file(
  TLS_APP tls_app.lds
  PATH "${CMAKE_CURRENT_LIST_DIR}"
  CMAKE_FIND_ROOT_PATH_BOTH
)
mark_as_advanced(TLS_APP)

# 确保 rootserver 的 tls_rootserver.lds 存在
find_file(
  TLS_ROOTSERVER tls_rootserver.lds
  PATH "${CMAKE_CURRENT_LIST_DIR}"
  CMAKE_FIND_ROOT_PATH_BOTH
)
mark_as_advanced(TLS_ROOTSERVER)

macro(gcc_print_file_name var file)
  separate_arguments(c_arguments UNIX_COMMAND "${CMAKE_C_FLAGS}")

  list(APPEND c_arguments "${CMAKE_C_COMPILE_OPTIONS_TARGET}${CMAKE_C_COMPILER_TARGET}")

  execute_process(
    COMMAND ${CMAKE_C_COMPILER} ${c_arguments} -print-file-name=${file}
    OUTPUT_VARIABLE ${var}
    ERROR_VARIABLE IgnoreErrorOutput
    OUTPUT_STRIP_TRAILING_WHITESPACE
  )
endmacro()

macro(find_libgcc_files)
  # find the compilers crtbegin and crtend files
  gcc_print_file_name(CRTBeginFile crtbegin.o)
  gcc_print_file_name(CRTEndFile crtend.o)
  gcc_print_file_name(libgcc_eh libgcc_eh.a)
  set(libgcc "-lgcc")
  if(NOT "${libgcc_eh}" STREQUAL "libgcc_eh.a")
      set(libgcc "${libgcc} -lgcc_eh")
  endif()
endmacro()

list(APPEND
  app_default_compile_flags
  -nostdinc -fno-pic -fno-pie
  -fno-stack-protector
  -fno-asynchronous-unwind-tables
  -frounding-math -Wa,--noexecstack
  -ftls-model=local-exec
  -Wmain
)

# 导入归档工具
include(make_archive)

# 所有需要归档的 app 应用在此链表中
set_property(GLOBAL PROPERTY ARCHIVE_APPDATA_FILE_LIST "")

# rootserver 和 app 通用的 编译链接选项
macro(common_compiler_options name)
  target_link_libraries(${name} PRIVATE common_interface)

  foreach(arg ${app_default_compile_flags})
    target_cc_option(${name} PRIVATE ${arg})
  endforeach()

  target_cc_option(${name} PRIVATE -fmacro-prefix-map=${APPLICATION_SOURCE_DIR}=${CMAKE_PROJECT_NAME})

  target_compile_options(${name} PRIVATE
    $<$<COMPILE_LANGUAGE:CXX>:-nostdinc++>
  )
  target_ld_options(${name} PRIVATE -static -nostdlib)
  target_link_libraries(${name} PRIVATE musllibc seminix)
endmacro()

# 定义 app
function(DeclareApp appname)
  set_property(
    TARGET ${appname}
    APPEND_STRING
    PROPERTY LINK_FLAGS " -Wl,-T ${TLS_APP} "
  )

  common_compiler_options(${appname})

  set_property(GLOBAL APPEND PROPERTY ARCHIVE_APPDATA_FILE_LIST "$<TARGET_FILE:${appname}>")
endfunction()

function(DeclareApps)
  foreach(arg ${ARGV})
    DeclareApp(${arg})
  endforeach()
endfunction()

# 定义 rootserver
function(DeclareRootserver rootservername archive_app)
  if(${archive_app})
    set(TLS_LDS ${TLS_ROOTSERVER})
  else()
    set(TLS_LDS ${TLS_APP})
  endif()

  set_property(
    TARGET ${rootservername}
    APPEND_STRING
    PROPERTY LINK_FLAGS " -Wl,-T ${TLS_LDS} "
  )

  common_compiler_options(${rootservername})

  get_property(ARCHIVE_APPDATA_FILE_LIST_PROPERTY GLOBAL PROPERTY ARCHIVE_APPDATA_FILE_LIST)
  make_appdata_archive(${ARCHIVE_APPDATA_FILE} ${ARCHIVE_APPDATA_FILE_LIST_PROPERTY})
  if(${archive_app})
    target_sources(${rootservername} PRIVATE ${ARCHIVE_APPDATA_FILE})
  endif()
  add_dependencies(${rootservername} appdata_archive)

  get_property(CORE_IMAGE_BINARY_PROPERTY GLOBAL PROPERTY CORE_IMAGE_BINARY)
  get_property(CORE_IMAGE_BINARY_FILE_PROPERTY GLOBAL PROPERTY CORE_IMAGE_BINARY_FILE)
  set(target_name ${rootservername}-image-${ARCH}-${target_platform})
  set(target_image_path ${APPLICATION_BINARY_DIR}/image/${target_name})
  add_custom_target(
    ${target_name} ALL
    COMMAND ${CMAKE_COMMAND}
      -E copy ${CORE_IMAGE_BINARY_FILE_PROPERTY} ${target_image_path}
    DEPENDS
      ${CORE_IMAGE_BINARY_PROPERTY}
    COMMENT
      "copy kernel image into binary core image"
    BYPRODUCTS
      ${target_image_path}
    WORKING_DIRECTORY
      ${APPLICATION_BINARY_DIR}
  )
  set_property(GLOBAL PROPERTY CORE_TARGET_IMAGE_PATH ${target_image_path})
  set_property(GLOBAL PROPERTY CORE_TARGET_NAME ${target_name})
endfunction()

# 寻找 libgcc 相关运行时 obj 文件
find_libgcc_files()

get_property(APP_CRUNTIME_DIR_PROPERTY GLOBAL PROPERTY APP_CRUNTIME_DIR)

set(CRTObjFiles "${APP_CRUNTIME_DIR_PROPERTY}/crt1.o ${APP_CRUNTIME_DIR_PROPERTY}/crti.o ${CRTBeginFile}")
set(FinObjFiles "${CRTEndFile} ${APP_CRUNTIME_DIR_PROPERTY}/crtn.o")

# libgcc has dependencies implemented by libc and so we use link groups to resolve these.
# This seems to be the same behaviour gcc has when building static binaries.
set(common_link_string "<LINK_FLAGS> ${CRTObjFiles} <OBJECTS> -Wl,--start-group \
  ${libgcc} <LINK_LIBRARIES> -Wl,--end-group ${FinObjFiles} -o <TARGET>"
)

set(
  CMAKE_C_LINK_EXECUTABLE
  "<CMAKE_C_COMPILER>  <FLAGS> <CMAKE_C_LINK_FLAGS> ${common_link_string}"
)

set(
  CMAKE_CXX_LINK_EXECUTABLE
  "<CMAKE_CXX_COMPILER>  <FLAGS> <CMAKE_CXX_LINK_FLAGS> ${common_link_string}"
)

set(
  CMAKE_ASM_LINK_EXECUTABLE
  "<CMAKE_ASM_COMPILER>  <FLAGS> <CMAKE_ASM_LINK_FLAGS> ${common_link_string}"
)
